/////////////////////////////////////////////////////////////////////////////
// Name:        mainframe.cpp
// Purpose:     
// Author:      Julian Smart
// Modified by: 
// Created:     18/02/2009 19:13:55
// RCS-ID:      
// Copyright:   
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include "wx/ffile.h"
#include "wx/filename.h"
#include "wx/aboutdlg.h"

////@begin includes
#include "mainpanel.h"
////@end includes

#include "blurtapp.h"
#include "preferencesdialog.h"
#include "mainframe.h"

#include "tts/tts_speechsettingsdialog.h"
#include "tts/tts_mp3settingsdialog.h"


////@begin XPM images
#include "sample.xpm"
////@end XPM images


/*
 * MainFrame type definition
 */

IMPLEMENT_CLASS( MainFrame, wxFrame )


/*
 * MainFrame event table definition
 */

BEGIN_EVENT_TABLE( MainFrame, wxFrame )
    EVT_BUTTON( wxID_OPEN, MainFrame::OnOpenClick )
    EVT_BUTTON( ID_SPEAK, MainFrame::OnSpeakClick )
    EVT_BUTTON( wxID_PREFERENCES, MainFrame::OnPreferencesClick )

////@begin MainFrame event table entries
    EVT_CLOSE( MainFrame::OnCloseWindow )
    EVT_IDLE( MainFrame::OnIdle )

    EVT_MENU( wxID_OPEN, MainFrame::OnOpenClick )

    EVT_MENU( ID_SPEAK, MainFrame::OnSpeakClick )
    EVT_UPDATE_UI( ID_SPEAK, MainFrame::OnSpeakUpdate )

    EVT_MENU( ID_CONVERT_TO_AUDIO, MainFrame::OnConvertToAudioClick )
    EVT_UPDATE_UI( ID_CONVERT_TO_AUDIO, MainFrame::OnConvertToAudioUpdate )

#if defined(__WXMAC__)
    EVT_MENU( wxID_ABOUT, MainFrame::OnAboutClick )
#endif

    EVT_MENU( wxID_EXIT, MainFrame::OnExitClick )

    EVT_MENU( wxID_PREFERENCES, MainFrame::OnPreferencesClick )

    EVT_MENU( ID_TEST_STANDALONE_SPEECH_SETTINGS, MainFrame::OnTestStandaloneSpeechSettingsClick )

    EVT_MENU( ID_TEST_STANDALONE_MP3_SETTINGS, MainFrame::OnTestStandaloneMp3SettingsClick )

#if defined(NOT_MAC)
    EVT_MENU( wxID_ABOUT, MainFrame::OnAboutClick )
#endif

////@end MainFrame event table entries

END_EVENT_TABLE()


/*
 * MainFrame constructors
 */

MainFrame::MainFrame()
{
    Init();
}

MainFrame::MainFrame( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
{
    Init();
    Create( parent, id, caption, pos, size, style );
}


/*
 * MainFrame creator
 */

bool MainFrame::Create( wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style )
{
////@begin MainFrame creation
    wxFrame::Create( parent, id, caption, pos, size, style );

    CreateControls();
    SetIcon(GetIconResource(wxT("sample.xpm")));
    Centre();
////@end MainFrame creation
    return true;
}


/*
 * MainFrame destructor
 */

MainFrame::~MainFrame()
{
////@begin MainFrame destruction
////@end MainFrame destruction
}


/*
 * Member initialisation
 */

void MainFrame::Init()
{
////@begin MainFrame member initialisation
    m_mainPanel = NULL;
    m_statusBar = NULL;
////@end MainFrame member initialisation
}


/*
 * Control creation for MainFrame
 */

void MainFrame::CreateControls()
{    
////@begin MainFrame content construction
    MainFrame* itemFrame1 = this;

    wxMenuBar* menuBar = new wxMenuBar;
    wxMenu* itemMenu3 = new wxMenu;
    itemMenu3->Append(wxID_OPEN, _("&Open...\tCtrl+O"), wxEmptyString, wxITEM_NORMAL);
    itemMenu3->AppendSeparator();
    itemMenu3->Append(ID_SPEAK, _("&Speak\tCtrl+P"), wxEmptyString, wxITEM_NORMAL);
    itemMenu3->Append(ID_CONVERT_TO_AUDIO, _("&Convert to Audio File"), wxEmptyString, wxITEM_NORMAL);
    itemMenu3->AppendSeparator();
#if defined(__WXMAC__)
    itemMenu3->Append(wxID_ABOUT, _("&About"), wxEmptyString, wxITEM_NORMAL);
#endif
    itemMenu3->Append(wxID_EXIT, _("E&xit"), wxEmptyString, wxITEM_NORMAL);
    menuBar->Append(itemMenu3, _("&File"));
    wxMenu* itemMenu11 = new wxMenu;
    itemMenu11->Append(wxID_PREFERENCES, _("&Preferences...\tCtrl+T"), wxEmptyString, wxITEM_NORMAL);
#if defined(NOT_MAC)
    itemMenu11->AppendSeparator();
#endif
    itemMenu11->Append(ID_TEST_STANDALONE_SPEECH_SETTINGS, _("Standalone Speech Settings Dialog..."), wxEmptyString, wxITEM_NORMAL);
    itemMenu11->Append(ID_TEST_STANDALONE_MP3_SETTINGS, _("Standalone MP3 Settings Dialog..."), wxEmptyString, wxITEM_NORMAL);
    menuBar->Append(itemMenu11, _("&View"));
#if defined(NOT_MAC)
    wxMenu* itemMenu16 = new wxMenu;
#if defined(NOT_MAC)
    itemMenu16->Append(wxID_ABOUT, _("&About"), wxEmptyString, wxITEM_NORMAL);
#endif
    menuBar->Append(itemMenu16, _("&Help"));
#endif
    itemFrame1->SetMenuBar(menuBar);

    m_mainPanel = new MainPanel( itemFrame1, ID_MAIN_PANEL, wxDefaultPosition, wxDefaultSize, wxNO_BORDER|wxTAB_TRAVERSAL );

    m_statusBar = new wxStatusBar( itemFrame1, ID_STATUSBAR, wxST_SIZEGRIP|wxNO_BORDER );
    m_statusBar->SetFieldsCount(2);
    itemFrame1->SetStatusBar(m_statusBar);

////@end MainFrame content construction

    m_wakeUpIdleTimer.Start(100);

    m_mainPanel->m_textCtrl->WriteText(_("Hello, and welcome to the wxWidgets text to speech demo. \
The wxTextToSpeech class makes it easy to add speech to your application, and you can choose \
between a number of engines, including eSpeak, Cepstral, and Microsoft SAPI.\
\n\nSelect text to just read that portion, or click on a starting position. You will probably have to configure the preferences first.\n\n\
Extract from \"Clifton in the Rain\", by Al Stewart.\n\n\
The rain came down in beads,\n\
Bouncing on the noses of the people from the train.\n\
A flock of salty ears,\n\
Sparkled in the traffic lights,\n\
Feet squelched soggy leaves across the grain.\n\
I took my love to Clifton, in the rain."));
    m_mainPanel->m_textCtrl->SetInsertionPoint(0);
}


/*
 * Should we show tooltips?
 */

bool MainFrame::ShowToolTips()
{
    return true;
}

/*
 * Get bitmap resources
 */

wxBitmap MainFrame::GetBitmapResource( const wxString& name )
{
    // Bitmap retrieval
////@begin MainFrame bitmap retrieval
    wxUnusedVar(name);
    return wxNullBitmap;
////@end MainFrame bitmap retrieval
}

/*
 * Get icon resources
 */

wxIcon MainFrame::GetIconResource( const wxString& name )
{
    // Icon retrieval
////@begin MainFrame icon retrieval
    wxUnusedVar(name);
    if (name == _T("sample.xpm"))
    {
        wxIcon icon( sample_xpm);
        return icon;
    }
    return wxNullIcon;
////@end MainFrame icon retrieval
}

/// Logs a line on the output window
void MainFrame::Log(const wxString& log)
{
    m_mainPanel->m_logCtrl->SetInsertionPointEnd();
    m_mainPanel->m_logCtrl->WriteText(log + wxT("\n"));
    m_mainPanel->m_logCtrl->ShowPosition(m_mainPanel->m_logCtrl->GetLastPosition());
}

/// Returns the original file or temporary filename if there is a selection
wxString MainFrame::GetFilenameForFileOrFragment(const wxString& originalFilename, wxTextCtrl* textCtrl)
{
    wxString filename(originalFilename);

    wxString fragment;
    long from, to;
    textCtrl->GetSelection(& from, & to);
    if (from != to)
    {
        fragment = textCtrl->GetStringSelection();
    }
    else
    {
        long insertionPoint = textCtrl->GetInsertionPoint();
        if (insertionPoint == textCtrl->GetLastPosition())
            insertionPoint = 0;

        //if (insertionPoint > 0)
        {
            fragment = textCtrl->GetRange(insertionPoint, textCtrl->GetLastPosition());
        }
    }

    if (fragment.IsEmpty())
        fragment = textCtrl->GetValue();

    if (!fragment.IsEmpty())
    {
        wxString tempFilename = wxFileName::CreateTempFileName(wxT("speech"));
        wxGetApp().AddTemporaryFile(tempFilename);
        wxFFile file;
        if (file.Open(tempFilename, wxT("w")))
        {
            file.Write(fragment);
            filename = tempFilename;
        }
    }

    return filename;
}


/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for wxID_OPEN
 */

void MainFrame::OnOpenClick( wxCommandEvent& WXUNUSED(event) )
{
    wxString msg = _("Choose a file");
    wxWindow* parent = this;
    wxString filename;
    wxString file(wxFileNameFromPath(filename));
    wxString path(wxPathOnly(filename));
    wxString wildcard(wxT("All files (*.*)|*.*"));

    wxFileDialog fileDlg(parent, msg, path, file, wildcard);
    if (fileDlg.ShowModal() == wxID_OK)
    {
        filename = fileDlg.GetPath();

        m_mainPanel->m_textCtrl->LoadFile(filename);
    }
}


/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_SPEAK
 */

void MainFrame::OnSpeakClick( wxCommandEvent& WXUNUSED(event) )
{
    if (!wxGetApp().m_speech.IsSpeaking())
    {
        wxString filename = GetFilenameForFileOrFragment(wxEmptyString, m_mainPanel->m_textCtrl);
        if (!filename.IsEmpty())
        {            
            wxGetApp().m_speech.SetProperty(wxTTS_PROP_INPUT_FORMAT, m_mainPanel->m_inputFormat->GetStringSelection());
            wxGetApp().m_speech.SetProperty(wxTTS_PROP_SIMPLIFY_PUNCTUATION, true);
            wxGetApp().m_speech.SpeakFile(filename);
        }
    }
    else
        wxGetApp().m_speech.Stop();
}


/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_CONVERT_TO_AUDIO
 */

void MainFrame::OnConvertToAudioClick( wxCommandEvent& WXUNUSED(event) )
{
    if (wxGetApp().GetBatchEncoder().IsBusy())
    {
        int ans = wxMessageBox(_("Really stop encoding?"), _("Text to Audio Conversion"), wxICON_QUESTION|wxYES_NO, this);
        if (ans == wxYES)
        {
            wxGetApp().GetBatchEncoder().Stop();
            SetStatusText(_("Encoding stopped manually."), 1);
            return;
        }
        else
            return;
    }
    
    // Do some checks
    // check whether the current engine requires these
    if (wxGetApp().GetSpeech().HasCapability(wxTTS_CAP_PROGRAM_LOCATION) && wxGetApp().GetSpeech().GetProgramLocation().IsEmpty())
    {
        wxMessageBox(_("Please install the selected speech engine and set the executable path in Speech preferences."), _("Text to Audio Conversion"), wxICON_INFORMATION|wxOK, this);
        return;
    }
    
    if (wxGetApp().GetMP3SettingsInfo().m_enabled && wxGetApp().GetMP3SettingsInfo().m_encoderPath.IsEmpty())
    {
        wxMessageBox(_("To generate MP3, please install LAME and set the executable path in MP3 preferences."), _("Text to Audio Conversion"), wxICON_INFORMATION|wxOK, this);
        return;
    }
    
    wxString wildcard(wxT("WAV files (*.wav)|*.wav|MP3 files (*.mp3)|*.mp3|All files (*.*)|*.*"));
    wxString audioFilename = wxFileSelector(_("Please select an audio file name."), wxEmptyString, wxEmptyString, wxT("mp3"), wildcard, wxFD_SAVE, this);
    if (audioFilename.IsEmpty())
        return;
    
    // Make the batch encoder speech class reflect the current play settings
    wxGetApp().GetBatchEncoder().GetSpeech().SetEngine(wxGetApp().GetSpeech().GetEngine());
    wxGetApp().GetBatchEncoder().GetSpeech().GetCurrentHandler()->GetProperties() = wxGetApp().GetSpeech().GetCurrentHandler()->GetProperties();
    
    wxGetApp().GetBatchEncoder().GetSpeech().SetProperty(wxTTS_PROP_INPUT_FORMAT, m_mainPanel->m_inputFormat->GetStringSelection());
    
    wxGetApp().GetBatchEncoder().GetMP3Encoder().SetInfo(wxGetApp().GetMP3SettingsInfo());

    wxGetApp().GetBatchEncoder().SetVerbosity(wxBATCHMP3ENCODER_VERBOSITY_VERBOSE);
    
    wxArrayString textFilenames;
    wxArrayString mp3Filenames;
    wxArrayString titles;
    wxID3InfoArray infoArray;
    
    wxString textFilename = GetFilenameForFileOrFragment(wxEmptyString, m_mainPanel->m_textCtrl);
    if (textFilename.IsEmpty())
        return;

    wxString mp3Filename = audioFilename;
    wxTextToSpeech::StripExtension(mp3Filename);
    mp3Filename += wxT(".mp3");
        
    textFilenames.Add(textFilename);
    mp3Filenames.Add(mp3Filename);
        
    wxID3Info id3Info;
    id3Info.SetTrackNumber(1);
    id3Info.SetGenre(wxT("Speech"));
    id3Info.SetTitle(wxT("wxWidgets Text To Speech Test"));
    id3Info.SetArtist(wxT("wxWidgets"));
    id3Info.SetComment(wxT("This is a test audio file created using the Blurt wxWidgets text to speech demo."));
    
    infoArray.Add(id3Info);
        
    wxGetApp().GetBatchEncoder().SetData(textFilenames, mp3Filenames, infoArray);
    wxGetApp().GetBatchEncoder().SetFrame(this, 1);
    
    int options = 0;
    if (wxGetApp().GetMP3SettingsInfo().m_enabled)
        options |= wxBATCHMP3ENCODER_OPTION_ENCODE_MP3;
    
    wxString log;
    bool success = wxGetApp().GetBatchEncoder().Start(options, & log);
    
    if (!log.IsEmpty())
    {
        Log(log);
    }
    
    if (!success)
    {
        wxMessageBox(_("Sorry, could not start encoding."), _("Text to Audio Conversion"), wxICON_INFORMATION|wxOK, this);
        
        Log(_("Reasons why audio generation might have failed include:"));
        Log(_("    - Incorrect speech engine and/or LAME installation. Check they are on your system."));
        Log(_("    - Incorrect specification of the speech engine and/or LAME binaries. Specify the full executable path in each case."));
        Log(_("See the manual for more information on where to obtain speech engines such as eSpeak, and LAME."));
    }
}

/*
 * wxEVT_UPDATE_UI event handler for ID_CONVERT_TO_AUDIO
 */

void MainFrame::OnConvertToAudioUpdate( wxUpdateUIEvent& event )
{
    wxString text(_("&Convert to Audio File\tShift+Ctrl+M"));
    if (wxGetApp().m_batchEncoder.IsBusy())
        text = _("Stop C&reating Audio File\tShift+Ctrl+M");
    event.Enable(true);
    event.SetText(text);
}

/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for wxID_EXIT
 */

void MainFrame::OnExitClick( wxCommandEvent& WXUNUSED(event) )
{
    Close(true);
}


/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for wxID_PREFERENCES
 */

void MainFrame::OnPreferencesClick( wxCommandEvent& WXUNUSED(event) )
{
    BlurtPreferencesDialog dialog(this);
    dialog.ShowModal();
}


/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for wxID_ABOUT
 */

void MainFrame::OnAboutClick( wxCommandEvent& WXUNUSED(event) )
{
    wxAboutDialogInfo info;
    info.SetName(_("Blurb"));
    info.SetVersion(_("1.0 Beta"));
    info.SetDescription(_("A demo of the wxTextToSpeech class wrapping eSpeak, SAPI and more."));
    info.SetCopyright(_T("(c) 2008 Julian Smart <julian@anthemion.co.uk>"));

    wxAboutBox(info);
}


/*
 * wxEVT_CLOSE_WINDOW event handler for ID_MAINFRAME
 */

void MainFrame::OnCloseWindow( wxCloseEvent& WXUNUSED(event) )
{
    if (wxGetApp().m_speech.IsSpeaking())
        wxGetApp().m_speech.Stop();

    if (wxGetApp().m_batchEncoder.IsBusy())
        wxGetApp().m_batchEncoder.Stop();

    wxGetApp().SaveTransformer();
    wxGetApp().DeleteTemporaryFiles();

    Destroy();
}


/*
 * wxEVT_UPDATE_UI event handler for ID_SPEAK
 */

void MainFrame::OnSpeakUpdate( wxUpdateUIEvent& event )
{
    if (wxGetApp().m_speech.IsSpeaking())
        event.SetText(wxT("&Stop"));
    else
        event.SetText(wxT("&Speak"));
}


/*
 * wxEVT_IDLE event handler for ID_MAINFRAME
 */

void MainFrame::OnIdle( wxIdleEvent& WXUNUSED(event) )
{
    if(wxGetApp().GetSpeech().IsSpeaking())
        wxGetApp().GetSpeech().ProcessInput();

    // If we're not batch processing, it'll just return false harmlessly.
    wxString log;
    bool finished = false;

    // If the current task has finished, this will go to the next task
    wxGetApp().m_batchEncoder.Process(& log, & finished);

    if (!log.IsEmpty())
    {
        Log(log);
    }

    /// We just finished
    if (finished)
    {
        // Check that all files were generated.
        wxArrayString nonExistant;
        size_t i;
        for (i = 0; i < wxGetApp().m_batchEncoder.GetOutputFilenames().GetCount(); i++)
        {
            wxString mp3Filename = wxGetApp().m_batchEncoder.GetOutputFilenames()[i];
            wxTextToSpeech::StripExtension(mp3Filename);
            
            wxString waveFilename = wxGetApp().m_batchEncoder.GetOutputFilenames()[i];
            wxTextToSpeech::StripExtension(waveFilename);
            
            mp3Filename += wxT(".mp3");
            waveFilename += wxT(".wav");
            
            if (wxGetApp().m_batchEncoder.GetOptions() & wxBATCHMP3ENCODER_OPTION_ENCODE_MP3)
            {
                if (!wxFileExists(mp3Filename))
                    nonExistant.Add(wxFileNameFromPath(mp3Filename));
            }
            else
            {
                if (!wxFileExists(waveFilename))
                    nonExistant.Add(wxFileNameFromPath(waveFilename));
            }
        }
        
        if (nonExistant.GetCount() > 0)
        {
            Log(_("The following files were not generated:"));
            for (i = 0; i < nonExistant.GetCount(); i++)
            {
                wxString filename = nonExistant[i];
                Log(filename);
            }
            Log(_("Perhaps an application was using one of the files, or there was a problem with your LAME installation."));
        }
        else
        {
            Log(_("Audio generation has completed successfully."));
        }
        SetStatusText(_("Audio file generation finished."), 1);
    }
}

void BlurtWakeUpIdleTimer::Notify()
{
    wxWakeUpIdle();
}



/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_TEST_STANDALONE_SPEECH_SETTINGS
 */

void MainFrame::OnTestStandaloneSpeechSettingsClick( wxCommandEvent& WXUNUSED(event) )
{
    wxTTSSpeechSettingsDialog dialog(wxGetApp().GetSpeechSettingsInfo(), this);

    if (dialog.ShowModal() == wxID_OK)
        wxGetApp().GetSpeechSettingsInfo() = dialog.GetInfo();
}


/*
 * wxEVT_COMMAND_MENU_SELECTED event handler for ID_TEST_STANDALONE_MP3_SETTINGS
 */

void MainFrame::OnTestStandaloneMp3SettingsClick( wxCommandEvent& WXUNUSED(event) )
{
    wxTTSMP3SettingsDialog dialog(wxGetApp().GetMP3SettingsInfo(), this);

    if (dialog.ShowModal() == wxID_OK)
        wxGetApp().GetMP3SettingsInfo() = dialog.GetInfo();
}

